import {
  Condition,
  DataType,
  FormItemScheme,
  SchemeItem,
} from '@/pages/form-model/interface';
import { deepSearch, uuid } from '@luband/jstools';
import { BehaviorSubject } from 'rxjs';
import { FormModel } from './form.model';
import { ObjectProperty } from './object.property';

export class FormProperty {
  key: string;
  root: FormModel;
  render: any;
  oneOf: Condition<boolean>;
  allOf: Condition<boolean>;
  watchProps: Condition;
  watchItemProps: Condition;
  namePath: string[];
  type: DataType;
  props: SchemeItem;
  formItemProps: FormItemScheme;
  $props: BehaviorSubject<SchemeItem>;
  $formItemProps: BehaviorSubject<FormItemScheme>;
  $valueChange: BehaviorSubject<FormProperty[]>;
  widget: string;

  constructor(
    config: FormItemScheme = {},
    namePath?: string[],
    root?: FormModel,
  ) {
    const {
      props,
      widget,
      render,
      oneOf,
      allOf,
      type,
      watchProps,
      watchItemProps,
      ...formItemProps
    } = config;
    const name = formItemProps?.name?.toString();
    this.namePath = name ? [...namePath, name] : namePath;
    this.key = uuid();
    this.render = render;
    this.root = root;
    this.oneOf = oneOf;
    this.allOf = allOf;
    this.watchProps = watchProps;
    this.watchItemProps = watchItemProps;
    this.type = type;
    this.widget = widget;
    this.$props = new BehaviorSubject<SchemeItem>(props);
    this.$formItemProps = new BehaviorSubject<FormItemScheme>(formItemProps);
    this.$valueChange = new BehaviorSubject<any>(
      formItemProps.initialValue ||
        this.namePath.reduce((p, n) => {
          return p?.[n];
        }, this.root?.formProps?.initialValues),
    );
    this.$props.subscribe((res) => {
      this.props = res;
    });
    this.$formItemProps.subscribe((res) => {
      this.formItemProps = res;
    });
    if (this.root?.required?.includes(this.namePath.join('/'))) {
      this.setRequired(true);
    }
  }

  getProperty(path: string): FormProperty {
    const pathList: string[] = path.split('/');
    return pathList.reduce((p: any, name: string) => {
      const [property] = deepSearch<FormProperty>(
        p ? [p] : [],
        (item: any) => item?.formItemProps?.name === name || item.name === name,
        'properties',
      );
      return property;
    }, this.root);
  }

  updateProps(props) {
    this.$props.next({ ...this.props, ...props });
    if (props.hasOwnProperty('value')) {
      this.setValue(props.value);
    }
  }

  updateItemProps(props) {
    this.$formItemProps.next({ ...this.formItemProps, ...props });
  }

  setRequired(required = true, message: string = '请输入${label}') {
    const rules = this.formItemProps.rules || [];
    const requiredRule: any = rules.find((item) =>
      item.hasOwnProperty('required'),
    );
    if (requiredRule) {
      requiredRule.required = required;
    } else {
      rules.push({ required, message });
    }
    this.updateItemProps({ rules });
  }

  setValue(value: any) {
    this.root.form.setFieldValue(this.namePath, value);
    this.$valueChange.next(value);
  }

  setHidden(hidden = true) {
    this.updateItemProps({ hidden });
  }

  setVisible(visible: boolean) {
    const [, path] = deepSearch<any>(
      [this.root],
      (item) => item === this,
      'properties',
    );
    const parent = path[path.length - 2];
    const parentProperty =
      parent.key === this.root.key
        ? this.root
        : (this.getProperty(parent.namePath.join('/')) as ObjectProperty);
    visible
      ? parentProperty?.addProperty(this)
      : parentProperty?.delProperty(this);
  }
}
